﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.WindowsAPICodePack.Shell;
using System.IO;
using System.Windows.Threading;
using System.Threading;
using System.Threading.Tasks;

namespace TouchExplorer.Entities
{
    public class DirectoryContentLoader
    {
        private CancellationTokenSource _dirCancelTokenSource;
        private CancellationToken _dirCancelToken;

        private CancellationTokenSource _filesCancelTokenSource;
        private CancellationToken _filesCancelToken;

        private CancellationTokenSource _filesBigCancelTokenSource;
        private CancellationToken _filesBigCancelToken;

        public ExplorerViewModel Model { get; private set; }

        public IEnumerable<TouchDirectory> Subdirectories { get; private set; }

        public IEnumerable<TouchFile> ChildFiles { get; private set; }

        public TouchDirectory LoadedDirectory { get; private set; }

        public bool Aborted { get; private set; }

        public bool Running { get; private set; }

        private Thread tDirectories;
        private Thread tFiles;

        private bool _directoriesLoaded;
        private bool _filesLoaded;

        public DirectoryContentLoader(ExplorerViewModel model)
        {
            this.Model = model;
        }

        public void Abort()
        {
            Aborted = true;
            //StopDirectoryLoading();
            //StopFilesLoading();
            //StopBigFilesLoading();
        }

        public void Execute(TouchDirectory directory)
        {
            if (!Running)
            {
                try
                {
                    LoadedDirectory = directory;
                    Running = true;
                    Model.Files.Clear();
                    Model.Directories.Clear();
                    this.SelectCurrentPath();
                    tDirectories = new Thread(() =>
                        {
                            try
                            {
                                this.GetDirectoryContent();
                                _directoriesLoaded = true;
                                CheckStatus();
                            }
                            catch (Exception ex)
                            {
                                App.LogError(ex);
                                throw;
                            }
                        });
                    tFiles = new Thread(() =>
                        {
                            try
                            {
                                this.GetCurrentFiles();
                                _filesLoaded = true;
                                CheckStatus();
                            }
                            catch (Exception ex)
                            {
                                App.LogError(ex);
                                throw;
                            }
                        });

                    tDirectories.Start();
                    tFiles.Start();
                }
                catch (Exception ex)
                {
                    App.LogError(ex);
                    throw;
                }
            }
        }

        private void CheckStatus()
        {
            if (_directoriesLoaded && _filesLoaded)
            {
                Running = false;
            }
        }

        private void SelectCurrentPath()
        {
            if (LoadedDirectory == null)
            {
                Model.CurrentPath.Clear();
            }
            else if (Model.CurrentPath.Contains(LoadedDirectory))
            {
                var idx = Model.CurrentPath.IndexOf(LoadedDirectory);
                for (int i = Model.CurrentPath.Count - 1; i > idx; i--)
                {
                    Model.CurrentPath.RemoveAt(i);
                }
            }

            if (!Model.CurrentPath.Contains(LoadedDirectory) && LoadedDirectory != null)
                Model.CurrentPath.Add(LoadedDirectory);
        }

        private void GetDirectoryContent()
        {
            if (LoadedDirectory == null)
            {
                Subdirectories = Model.RootDirectories;
            }
            else
            {
                Subdirectories = LoadedDirectory.GetDirectories(dir =>
                {
                    if (dir.Info == null) return true;

                    if ((dir.Info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden && dir.Info.Root.FullName != dir.Info.FullName)
                        return false;

                    if ((dir.Info.Attributes & FileAttributes.System) == FileAttributes.System && dir.Info.Root.FullName != dir.Info.FullName)
                        return false;

                    return true;
                });
            }

            if (Aborted) return;

            foreach (var dir in Subdirectories)
            {
                if (Aborted) return;
                Model.Dispatcher.Invoke(new Action(() =>
                    {
                        if (Aborted)
                            return;

                        Model.Directories.Add(dir);
                        System.Windows.Forms.Application.DoEvents();
                    }));
            }

            System.Windows.Forms.Application.DoEvents();
        }

        private void GetCurrentFiles()
        {
            if (LoadedDirectory == null)
            {
                ChildFiles = Model.RootFiles;
                if (ChildFiles == null)
                    return;
            }
            else
            {
                var lstFiles = LoadedDirectory.GetFiles(
                    f =>
                        {
                            if ((f.Info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) return false;

                            if ((f.Info.Attributes & FileAttributes.System) == FileAttributes.System) return false;

                            return true;
                        }).ToList();
                ChildFiles = lstFiles;

                var def = default(KeyValuePair<string, IComparer<TouchFile>>);
                if (Model.SelectedSorting != null)
                {
                    lstFiles.Sort(Model.SelectedSorting);
                }
            }

            foreach (var touchFile in ChildFiles)
            {
                if (Aborted) return;
                Model.Dispatcher.Invoke(
                    new Action(
                        () =>
                        {
                            if (Aborted) return;
                            Model.Files.Add(touchFile);
                            System.Windows.Forms.Application.DoEvents();
                        }));
            }

            System.Windows.Forms.Application.DoEvents();

            if (Aborted) return;
            //LoadFilesImages();
        }

        /*
        private void TaskLoad(ref CancellationTokenSource reftokensource, ref CancellationToken reftoken, Action act)
        {
            reftokensource = new CancellationTokenSource();
            reftoken = reftokensource.Token;
            Task.Factory.StartNew(act, reftoken);
        }

        private void LoadDirectoriesImages()
        {
            if (Aborted) return;
            this.DoLoadDirectoriesImages();
            //this.StopDirectoryLoading();

            //this.TaskLoad(ref _dirCancelTokenSource, ref _dirCancelToken, DoLoadDirectoriesImages);
        }

        private void DoLoadDirectoriesImages()
        {
            if (Aborted) return;
            
            //for (int i = 0; i < elts.Count; i++)
            Parallel.ForEach(Subdirectories, (dir, state) =>
            {
                if (Aborted || _dirCancelToken.IsCancellationRequested)
                {
                    state.Break();
                    //break;
                }

                //var dir = elts[i];
                if (!dir.ImageLoaded)
                {
                    if (dir.Shell == null)
                    {
                        dir.Shell = ShellObject.FromParsingName(dir.FullPath);
                    }

                    dir.GetImage();
                }
            });
        }

        private void StopDirectoryLoading()
        {
            if (this._dirCancelTokenSource != null && this._dirCancelToken.CanBeCanceled)
            {
                this._dirCancelTokenSource.Cancel();
                //this._dirCancelToken.WaitHandle.WaitOne();
            }

            _dirCancelTokenSource = null;
        }

        public void LoadFilesImages()
        {
            if (Aborted) return;
            DoLoadFilesImages();
            //this.StopFilesLoading();
            //this.TaskLoad(ref _filesCancelTokenSource, ref _filesCancelToken, DoLoadFilesImages);
        }

        private void StopFilesLoading()
        {
            if (this._filesCancelTokenSource != null && this._filesCancelToken.CanBeCanceled)
            {
                this._filesCancelTokenSource.Cancel();
                //this._filesCancelToken.WaitHandle.WaitOne();
            }

            _filesCancelTokenSource = null;
        }

        private void DoLoadFilesImages()
        {
            if (Aborted) return;
            
            //for (int i = 0; i < elts.Count; i++)
            Parallel.ForEach(
                ChildFiles,
                (file, state) =>
                {
                    if (Aborted || _filesCancelToken.IsCancellationRequested)
                    {
                        state.Break();
                        //break;
                    }

                    //var file = elts[i];
                    if (!file.ImageLoaded)
                    {
                        if (file.Shell == null)
                        {
                            file.Shell = ShellObject.FromParsingName(file.Info.FullName);
                        }
                        file.GetImage();
                    }
                });
        }

        public void LoadFilesBigImages()
        {
            this.DoLoadFilesBigImages();
            //this.StopFilesLoading();
            //this.TaskLoad(ref _filesBigCancelTokenSource, ref _filesBigCancelToken, DoLoadFilesBigImages);
        }

        private void StopBigFilesLoading()
        {
            if (this._filesBigCancelTokenSource != null && this._filesBigCancelToken.CanBeCanceled)
            {
                this._filesBigCancelTokenSource.Cancel();
                //this._filesCancelToken.WaitHandle.WaitOne();
            }

            _filesBigCancelTokenSource = null;
        }

        private void DoLoadFilesBigImages()
        {
            if (Aborted) return;

            Parallel.ForEach(
                ChildFiles,
                (file, state) =>
                {
                    if (Aborted || _filesBigCancelToken.IsCancellationRequested)
                    {
                        state.Break();
                        //break;
                    }

                    //var file = elts[i];
                    if (!file.BigImageLoaded)
                    {
                        if (file.Shell == null)
                        {
                            file.Shell = ShellObject.FromParsingName(file.Info.FullName);
                        }
                        file.GetBigImage();
                    }
                });
        }*/

    }
}
